using System;
using CatLib.Container;
using GameFramework.Extensions;
using GameFramework.Fsm;
using GameFramework.Procedure.Fsm;
using GameFramework.Reference;

namespace GameFramework.Gameplay
{
    /// <summary>
    /// 玩法流程
    /// </summary>
#if USE_GAMEPLAY_PROCEDURE
    [GameFramework.Procedure.Attribute.Procedure]
#endif
    public sealed class GameplayProcedure : AFsmProcedure
    {
        private const string Key_GameplayId = "GameplayProcedure.GameplayId";
        private const string Key_GameplayParameters = "GameplayProcedure.GameplayParameters";
        private const string Key_ErrorNextProcedure = "GameplayProcedure.ErrorNextProcedure";
        private const string Key_FinishNextProcedure = "GameplayProcedure.FinishNextProcedure";

        private IGameplay m_Gameplay;
        private IGameplayParameters m_GameplayParameters;
        private Type m_ErrorNextProcedure;
        private Type m_FinishNextProcedure;

        public static void ChangeGameplayProcedure(IFsm fsm, uint gameplayId, IGameplayParameters parameters, Type finishNextProcedure, Type errorNextProcedure)
        {
            fsm.Blackboard.SetUInt(Key_GameplayId, gameplayId);
            fsm.Blackboard.SetObject(Key_GameplayParameters, parameters);
            fsm.Blackboard.SetObject(Key_FinishNextProcedure, finishNextProcedure);
            fsm.Blackboard.SetObject(Key_ErrorNextProcedure, errorNextProcedure);
            ((IFsmOperator)fsm).ChangeState(typeof(GameplayProcedure));
        }

        public override void OnEnter(IFsm fsm)
        {
            var gameplayId = fsm.Blackboard.GetUInt(Key_GameplayId);
            m_Gameplay = CatLib.App.That.Make<IGameplayFactory>().Create(gameplayId);
            m_GameplayParameters = fsm.Blackboard.GetObject<IGameplayParameters>(Key_GameplayParameters);
            m_ErrorNextProcedure = fsm.Blackboard.GetObject<Type>(Key_ErrorNextProcedure);
            m_FinishNextProcedure = fsm.Blackboard.GetObject<Type>(Key_FinishNextProcedure);
            
            fsm.Blackboard.Remove(Key_GameplayId);
            fsm.Blackboard.Remove(Key_GameplayParameters);
            fsm.Blackboard.Remove(Key_ErrorNextProcedure);
            fsm.Blackboard.Remove(Key_FinishNextProcedure);
            
            var container = CatLib.App.That;

            if (m_GameplayParameters != null)
            {
                container.BindSingletonInstance(m_GameplayParameters).Alias(container.Type2Service(m_GameplayParameters.GetType()));
            }

            if (m_Gameplay != null)
            {
                try
                {
                    m_Gameplay.Initialize();
                    container.Inject(m_Gameplay);
                    container.BindSingletonInstance(m_Gameplay);
                }
                catch (Exception e)
                {
                    Log.Exception(e);
                }
            }

            base.OnEnter(fsm);
        }

        public override void OnLeave(IFsm fsm)
        {
            base.OnLeave(fsm);
            
            var container = CatLib.App.That;
            
            if (m_Gameplay != null)
            {
                try
                {
                    container.Unbind<IGameplay>();
                    m_Gameplay.Dispose();
                }
                catch (Exception e)
                {
                    Log.Exception(e);
                }

                m_Gameplay = null;
            }

            if (m_GameplayParameters != null)
            {
                container.Unbind<IGameplayParameters>();
                
                m_GameplayParameters.Recycle();
                m_GameplayParameters = null;
            }
        }

        public override void OnUpdate(IFsm fsm, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);

            if (m_Gameplay == null)
                Change(fsm, m_ErrorNextProcedure);
            else if (m_Gameplay.IsFinish)
                Change(fsm, m_FinishNextProcedure);
        }
    }

    public static class GameplayProcedureExtensions
    {
        /// <summary>
        /// 切换至玩法流程
        /// </summary>
        public static void ChangeGameplayProcedure(this IFsm fsm, uint gameplayId, IGameplayParameters parameters, Type finishNextProcedure, Type errorNextProcedure)
        {
            GameplayProcedure.ChangeGameplayProcedure(fsm, gameplayId, parameters, finishNextProcedure, errorNextProcedure);
        }

        /// <summary>
        /// 切换至玩法流程
        /// </summary>
        public static void ChangeGameplayProcedure<TNextProcedure, TErrorProcedure>(this IFsm fsm, uint gameplayId, IGameplayParameters parameters)
            where TNextProcedure : AFsmProcedure
            where TErrorProcedure : AFsmProcedure
        {
            GameplayProcedure.ChangeGameplayProcedure(fsm, gameplayId, parameters, typeof(TNextProcedure), typeof(TErrorProcedure));
        }
    }
}